include ../_util-fns

:marked
  In this chapter we'll setup the environment for testing our sample application and write a few easy Jasmine tests of the app's simplest parts.

  本章中，我们将为范例应用设置测试环境，并针对应用程序中最简单的部分，写几个容易点儿的Jasmine测试。

  We'll learn:

  我们会学到：

  - to test one of our application files

  - 测试我们应用程序中的一个文件

  - why we prefer our test files to be next to their corresponding source files

  - 为什么我们更喜欢把测试文件放在它对应的源码旁边

  - to run tests with an `npm` command

  - 用`npm`命令运行测试

  - load the test file with SystemJS

  - 使用SystemJs加载测试文件

.callout.is-helpful
  header Prior Knowledge
  header 预备知识
  :marked
    The Unit Testing chapters build upon each other. We recommend reading them in order.
    We're also assuming that you're already comfortable with basic Angular 2 concepts and the tools
    we introduced in the [QuickStart](../quickstart.html) and
    the [Tour of Heroes](../tutorial/) tutorial
    such as <code>npm</code>, <code>gulp</code>, and <code>lite-server</code>.

    这一章单元测试是在其它章节的基础上写的。我们建议你按顺序阅读它们。同时，我们假设你已经对Angular 2的原理、[“快速起步”](../quickstart.html)和
    [英雄指南](../tutorial)中介绍的<code>npm</code>、<code>gulp</code>和<code>lite-server</code>等工具都已经有所了解。

.l-main-section
:marked
  ## Create the test-runner HTML

  ## 创建测试运行器(test-runner)HTML

  Locate the folder that contains the application `index.html` for your testing copy of Tour of Heroes.

  在《英雄指南》的测试拷贝版里找到含有`index.html`的目录。

  Create a new, sibling HTML file, ** `unit-tests.html` ** and copy over the same basic material from the  `unit-tests.html` in the [Jasmine 101](./jasmine-testing-101.html) chapter.

  新建一个同级的HTML文件**`unit-tests.html`**，从[Jasmine 101](./jasmine-testing-101.html)一章中，将`unit-tests.html`里面的基本素材拷贝进该文件。

+makeExample('testing/ts/unit-tests-2.html', 'test-runner-base', 'unit-tests.html')

:marked
  We're picking up right where we left off. All we've done is change the title.

  我们把上次的重点作为起点。那时我们只修改完了标题。

.l-main-section
:marked
  ## Update `package.json` for testing

  ## 为做测试更新`package.json`

  We must install the Jasmine package as well:

  我们还必须安装Jasmine包：

pre.prettyprint.lang-bash
  code npm install jasmine-core --save-dev --save-exact

.alert.is-important Be sure to install <code>jasmine-core</code> , not <code>jasmine</code>!

.alert.is-important 请确保安装的是<code>jasmine-core</code>，而不是<code>jasmine</code>！

.l-main-section
:marked
  ## Configure `lite-server` for serving our test harness
  
  ## 配置`lite-server`来提供测试环境

:marked
  First create a configuration file for serving up our test harness through `lite-server`.
  
  首先为用来提供测试环境的`lite-server`创建一个配置文件。

+makeExample('testing/ts/liteserver-test-config.json', '', 'liteserver-test-config.json')

:marked
  Let's make one more change to the `package.json` script commands.

  让我们在`package.json`的脚本命令区再做一项修改。

  **Open the `package.json` ** and scroll to the `scripts` node and add the following two entries:

  **打开`package.json`**，滚动到`scripts`节点，添加下面两行：

code-example(format="").
  "lite-server-test": "lite-server --config=liteserver-test-config.json",
  "test": "tsc && concurrently \"npm run tsc:w\" \"npm run lite-server-test\" "

:marked
  The `npm test` command will launch `lite-server` and open a browser to the `unit-tests.html` page we just wrote. It will also take care of recompiling your source code and reloading your browser after any change.

  这条`npm test`命令将启动`lite-server`，并在浏览器中打开我们刚写的`unit-tests.html`页面。它还会在做出任何更改之后重新编译源代码，并刷新浏览器。

.l-main-section
:marked
  ## First app tests

  ## 第一个应用程序测试

  We can start testing *some* of our app right away. For example, we can test the `Hero` interface:

  我们现在可以开始测试应用程序中的*某些*代码了。比如，我们可以测试`Hero`接口：

+makeExample('testing/ts/app/hero.ts')

:marked
  Let's add a couple of simple tests in a new file.

  让我们在一个新文件里面添加一些简单的测试。

+makeExample('testing/ts/app/hero.spec.ts', 'base-hero-spec')

:marked
  That's the basic Jasmine we learned back in "Jasmine 101".

  上面是我们在“Jasmine 101”学习的基本Jasmine知识。

  Notice that we surrounded our tests with ** `describe('Hero')` **.

  请注意，我们把这些测试包裹在**`describe('Hero')`**中了。

  **By convention, our test always begin with a `describe` that identifies the application part under test.**

  **按惯例，我们的测试总会以`describe`开始，它标识出所测的是应用程序的哪个部分。**

  The description should be sufficient to identify the tested application part and its source file.
  Almost any convention will do as long as you and your team follow it consistently and are never confused.

  这个说明要足以标识出所测的部件和相关源码。无论采用什么约定，都应该让你和你的团队始终如一的遵循它，并且永不混淆它们。

  But we haven't saved this test yet.

  不过，我们现在还没保存这个测试呢。

.l-main-section
:marked
  ## Where do tests go?

  ## 测试文件放在哪里？

  Some people like to keep their tests in a `tests` folder parallel to the application source folder.

  有人喜欢把他们的测试保存在与应用程序源码平级的`tests`目录下。

  We are not those people. We like our unit tests to be close to the source code that they test. We prefer this approach because

  但我们不喜欢。我们更喜欢让这些单元测试放得离它们要测的源码尽可能近一点。我们更喜欢这种方式是因为：

  - The tests are easy to find

  - 更容易找到测试

  - We see at a glance if an application part lacks tests.

  - 我们只要扫一眼就能知道应用程序中哪部分缺少测试

  - Nearby tests can teach us about how the part works; they express the developers intention and reveal how the developer thinks the part should behave under a variety of circumstances.

  - 附近的测试能告诉我们这部分是如何工作的，它们能表达开发者的意图，并揭示出开发者认为在各种情况下这部分该有怎样的行为。

  - When we move the source (inevitable), we remember to move the test.

  - 当我们移动源码时(难免的)，总能记得同时移动测试。

  - When we rename the source file (inevitable), we remember to rename the test file.

  - 当我们重命名源码文件时(难免的)，总能记得同时改名测试文件。

  We can't think of a downside. The server doesn't care where they are.  They are easy to find and distinguish from application files when named conventionally.

  我们想不到有什么负面效果。服务器不在乎它们放在哪里。如果按照约定命名它们，它们也很容易从应用本身的文件中找到和区分出来。

.l-sub-section
  :marked
    You may put your tests elsewhere if you wish.
    We're putting ours inside the app, next to the source files that they test.

    如果喜欢，你也可以自己的测试放在其它地方。
    反正我们是把它们放进了我们的应用代码中，紧跟着它们所要测试的源码文件。

.l-main-section
:marked
  ## First spec file

  ## 第一个规约文件

  **Create** a new file, ** `hero.spec.ts` ** in `app` next to `hero.ts`.

  在`app`下，紧挨着`hero.ts`，**创建**一个名叫`hero.spec.ts`的新文件。

  Notice the ".spec" suffix in the test file's filename, appended to the name of the file holding the application part we're testing.

  注意，测试文件名称中的“.spec”后缀，被追加到了被测试的应用程序部件的基本名后面。

.alert.is-important  
  :marked
    All of our unit test files follow this .spec naming pattern.
    
    我们所有的单元测试文件都遵循了这种.spec命名模式。

:marked
  Save the tests we just made in `hero.spec.ts`:

  把测试保存到我们刚新建的`hero.spec.ts`文件：

+makeExample('testing/ts/app/hero.spec.ts', 'base-hero-spec')

:marked
  ### Import the part we're testing

  ### 导入我们准备测试的部件

  We have an `import {Hero} from './hero' ` statement.

  这里我们有个`import {Hero} from './hero' `语句。

  If we forgot this import, a TypeScript-aware editor would warn us, with a squiggly red underline, that it can't find the definition of the `Hero` interface.

  如果我们忘记了这条导入语句，支持TypeScript的编辑器就会通过一个红色曲线警告我们：它找不到`Hero`接口的定义。

  ### Update unit-tests.html

  ### 更新unit-tests.html

  Next we update the `unit-tests.html` with a reference to our new `hero.spec.ts` file. Delete the inline test code.  The revised pertinent HTML looks like this:

  接下来我们在`unit-tests.html`中用一个到这个新的`hero.spec.ts`文件的引用代替原来的内联测试代码。
  修改过的HTML看起来是这样的：

+makeExample('testing/ts/unit-tests-2.html', 'load-hero-and-spec')(format=".")

:marked
  ### Run and Fail
  ### 运行，并失败

  Look over at the browser (lite-server will have reloaded it).  The browser displays

  看看浏览器(lite-server应该已经刷新了它)。其中显示的是：

figure.image-display
  img(src='/resources/images/devguide/first-app-tests/Jasmine-not-running-tests.png' style="width:400px;" alt="Jasmine not running any tests")

:marked
  That's Jasmine saying "**things are _so_ bad that _I'm not running any tests_.**"

  这是Jasmine在说“**事情_很糟_，因为_我没找到任何测试_。**”

  Open the browser's Developer Tools (F12, Ctrl-Shift-i). There's an error:

  打开浏览器开发工具(F12或Ctrl-Shift-I)就会看到一个错误：

code-example(format="" language="html").
  Uncaught ReferenceError: System is not defined

.l-main-section
:marked
  ## Load tests with SystemJS

  ## 使用SystemJS加载这些测试

  The immediate cause of the error is the `export` statement in `hero.ts`.
  That error was there all along.
  It wasn't a problem until we tried to `import` the `Hero` interface in our tests.

  导致这个错误的直接原因，就是`hero.ts`中的`export`语句。
  那个错误一直都存在。
  除非我们尝试把`Hero`接口`import`到测试中，否则这个问题会一直存在。

  Our test environment lacks support for module loading.
  Apparently we can't simply load our application and test scripts like we do with 3rd party JavaScript libraries.

  我们的测试环境中缺少了对模块加载的支持。
  显然，我们没法像使用第三方JavaScript库那样简单的加载起应用代码和测试脚本。

  We are committed to module loading in our application.
  Our app will call `import`.  Our tests must do so too.

  我们得在本应用中引入模块加载机制。
  我们在应用代码中要调用`import`，测试代码中也要如此。

  We add module loading support in four steps:

  我们要通过四步儿来添加对模块加载的支持。

  1. add the *SystemJS* module management library

  1. 添加*SystemJS*模块管理库

  1. configure *SystemJS* to look for JavaScript files by default

  1. 配置*SystemJS*来默认查找JavaScript文件

  1. import our test files

  1. 导入我们的测试文件

  1. tell Jasmine to run the imported tests

  1. 让Jasmine运行已导入的测试

  These steps are all clearly visible, in exactly that order, in the following lines that
  replace the `<body>` contents in `unit-tests.html`:

  在`unit-tests.html`中用于替换`<body>`内容的那些代码行中，这些步骤清晰可见：

+makeExample('testing/ts/unit-tests-3.html', 'systemjs')(format=".")

:marked
  Look in the browser window. Our tests pass once again.

  到浏览器窗口中看看，我们的测试又一次通过了。

figure.image-display
  img(src='/resources/images/devguide/first-app-tests/test-passed-once-again.png' style="width:400px;" alt="Tests passed once again")


.l-main-section
:marked
  ## Observations

  ## 回顾

  ### System.config
  System.js demands that we specify a default extension for the filenames that correspond to whatever it is asked to import.
  Without that default, it would translate an import statement such as `import {Hero} from './hero'` to a request for the file named `hero`.
  Not `hero.js`. Just plain `hero`.  Our server error with "404 - not found" because it doesn't have a file of that name.

  System.js强烈要求我们为想import的那些文件指定一个默认扩展名。
  如果没有默认扩展名，它就会把import语句翻译成`import {Hero} from './hero'`来请求一个名叫`hero`的文件，而不是`hero.js`。服务器就会返回一个“404 - not found”，
  因为它找不到那个不带扩展名的文件。

  Once configured with a default extension of 'js',&nbsp;  SystemJS requests `hero.js` which *does* exist and is promptly returned by our server.

  一旦把默认扩展名配置为'js'，SystemJS就会请求`hero.js`，它存在，并且立即被服务器成功返回。

  ### Asynchronous System.import

  ### 异步System.import

  The call to `System.import` shouldn't surprise us but its asynchronous nature might.
  If we ponder this for a moment, we realize that it must be asynchronous because
  System.js may have to fetch the corresponding JavaScript file from the server.
  Accordingly, `System.import` returns a promise and we must wait for that promise to resolve.
  Only then can Jasmine start evaluating the imported tests.

  对`System.import`的调用不会让我们感到意外 —— 除了它的异步性。
  如果我们仔细考虑一会儿，就会认识到它必须是异步的，因为SystemJS不得不从服务器上取得相应的JavaScript文件。
  因此，`System.import`会返回一个承诺(Promise)，我们得等待这个承诺被解决。
  然后，Jasmine才能开始执行这些导入的测试。

  ### window.onload
  Jasmine doesn't have a `start` method.  It wires its own start to the browser window's `load` event.
  That makes sense if we're loading our tests with script tags.
  The browser raises the `load` event when it finishes loading all scripts.

  Jasmine没有一个`start`方法。它把自己的启动代码挂接到了浏览器窗口的`load`事件上。
  如果我们通过`script`标签来启动测试，这样的设计自然很合理。
  当浏览器加载完所有脚本时，它就会触发`load`事件。

  But we're not loading test scripts inline anymore.
  We're using the SystemJS module loader and it won't be done until long after the browser raised the `load` event.
  Meanwhile, Jasmine started and ran to completion, with no tests to evaluate, before the import completed.

  但我们没有用内联的方式加载测试脚本了。
  我们在使用SystemJS模块加载器，除非浏览器触发了`load`事件，否则它什么也不会做。
  同时，在这些import完成之前，Jasmine自己已经启动并且运行结束了，不过一个测试也没有执行。

  So we must wait until the import completes and only then call the window `onLoad` handler.
  Jasmine re-starts, this time with our imported test queued up.

  所以我们必须等待import执行完，然后才调用window的`onLoad`处理器。
  Jasmine重新启动，但这次它带着我们导入的这个测试队列。

.l-main-section
:marked
  ## What's Next?

  ## 下一步呢？

  We are able to test a part of our application with simple Jasmine tests.
  The part was a stand-alone interface that made no mention or use of Angular.

  我们已经能用简单的Jasmine测试来测试应用的一部分。
  这部分是一个独立的接口，它没有引用或使用任何来自Angular的东西。

  That's not rare but it's not typical either.
  Most of our application parts make some use of the Angular framework.
  Let's test a *pipe* class that does rely on Angular.

  这虽然不算罕见，但也不够典型。
  我们应用程序中的大部分都会使用到Angular框架的某些东西。
  我们再来测试一个*管道*类，它依赖于Angular。
